//
// Created by Administrator on 2025/8/15.
//

#include "mqttclient.h"

// --- 1. 包含所有必要的头文件 ---
#include "FreeRTOS.h"          // FreeRTOS 内核
#include "task.h"              // FreeRTOS 任务管理
#include "lwip/sockets.h"      // LwIP BSD Socket API
#include "lwip/netdb.h"        // LwIP getaddrinfo DNS API

#include "MQTTPacket.h"        // Paho MQTT 核心库

#include "printf_rtos.h"       // 你的线程安全打印函数
#include "mqttAPI.h"


#define NEWLAND_SERVER_DISCONNECTED 0
#define NEWLAND_SERVER_CONNECTED 1
// 定义一个全局变量来保存当前活动的 socket，以便简化的 mqtt_transport_read 函数可以访问它
static int g_mqtt_socket = -1;
static int g_mqtt_socket_status = -1;

int mqtt_transport_read(unsigned char* buf, int len)
{
    // 检查 socket 句柄是否有效
    int bytes_read=0;
    if (g_mqtt_socket < 0) {
        g_mqtt_socket_status=NEWLAND_SERVER_DISCONNECTED;
        return -1;
    }
    bytes_read=recv(g_mqtt_socket, buf, len, 0);
    if (bytes_read>0)
    {
        g_mqtt_socket_status=NEWLAND_SERVER_CONNECTED;
        return bytes_read;
    }
    if (bytes_read==0)
    {
        printf_rtos("Transport: Peer closed connection.\n");
        g_mqtt_socket_status=NEWLAND_SERVER_DISCONNECTED;
        return -1;
    }
    if (errno == EAGAIN)
    {
        return 0;
    }
    printf_rtos("Transport: recv error, errno=%d. Connection lost.\n", errno);
    g_mqtt_socket_status=NEWLAND_SERVER_DISCONNECTED;
    return -1;
}

/**
 * @brief 为 Paho 库封装的底层写入函数
 */
int mqtt_transport_write(unsigned char* buf, int len)
{
    if (g_mqtt_socket < 0) {
        return -1;
    }
    return send(g_mqtt_socket, buf, len, 0);
}

// --- 4. 核心的 MQTT 客户端任务 ---

void mqtt_client_task(void* argument)
{
    (void)argument; // 避免 unused parameter 警告

    printf_rtos("MQTT Client Task started.\n");

    // --- 任务内变量声明 ---
    unsigned char send_buf[256]={0};      // MQTT 发送缓冲区
    unsigned char recv_buf[256]={0};      // MQTT 接收缓冲区
    int ret=-1,setsocketret=-1;                          // 通用函数返回值
    uint32_t count = 0;               // 发布消息的计数器
    TickType_t last_publish_tick = xTaskGetTickCount(); // 用于周期性发布的独立计时器
    TickType_t last_activity_tick = xTaskGetTickCount();  // 用于心跳保活的活动计时器
    // --- 任务主循环，负责处理连接、断线重连 ---
    for(;;)
    {
        // ======================= 阶段 A: TCP 连接 =======================
        printf_rtos("Phase 1: TCP Connecting...\n");

        // DNS 解析
        struct addrinfo hints = {0};
        struct addrinfo *res = NULL;
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM; // MQTT 基于 TCP
        ret = getaddrinfo(MQTT_BROKER_HOSTNAME, NULL, &hints, &res);
        if (ret != 0) {
            printf_rtos("DNS lookup failed for %s, retrying...\n", MQTT_BROKER_HOSTNAME);
            vTaskDelay(pdMS_TO_TICKS(MQTT_RECONNECT_DELAY_MS));
            continue; // 回到主循环顶部，开始下一次重连尝试
        }

        // 准备服务器地址结构体
        struct sockaddr_in *server_addr = (struct sockaddr_in *)res->ai_addr;
        server_addr->sin_port = htons(MQTT_BROKER_PORT);

        // 创建 TCP Socket
        g_mqtt_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (g_mqtt_socket < 0) {
            printf_rtos("Failed to create socket.\n");
            freeaddrinfo(res);
            vTaskDelay(pdMS_TO_TICKS(MQTT_RECONNECT_DELAY_MS));
            continue;
        }
        struct timeval tv;
        tv.tv_sec = 1; tv.tv_usec = 0;
        setsocketret=setsockopt(g_mqtt_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));

        // 连接到 Broker 服务器
        ret = connect(g_mqtt_socket, res->ai_addr, res->ai_addrlen);
        freeaddrinfo(res); // DNS 结果使用完毕后，立即释放内存
        if (ret != 0) {
            printf_rtos("Failed to connect to broker.\n");
            closesocket(g_mqtt_socket); g_mqtt_socket = -1;
            vTaskDelay(pdMS_TO_TICKS(MQTT_RECONNECT_DELAY_MS));
            continue;
        }
        printf_rtos("TCP Connected to %s.\n", MQTT_BROKER_HOSTNAME);

        // ======================= 阶段 B: MQTT 协议连接 =======================
        printf_rtos("Phase 2: MQTT Connecting...\n");

        // 准备 MQTT CONNECT 包的参数
        MQTTPacket_connectData connect_data = MQTTPacket_connectData_initializer;
        connect_data.clientID.cstring = MQTT_CLIENT_ID;         // 设置客户端ID
        connect_data.keepAliveInterval = MQTT_KEEP_ALIVE_INTERVAL_S; // 设置心跳间隔
        connect_data.cleansession = 1;                           // 设置为 Clean Session

        // 使用 Paho 库的函数将连接请求“打包”成二进制数据
        int len = MQTTSerialize_connect(send_buf, sizeof(send_buf), &connect_data);
        // 通过我们封装的 mqtt_transport_write 发送出去
        mqtt_transport_write(send_buf, len);

        // 等待并解析 CONNACK (连接回执)
        ret = MQTTPacket_read(recv_buf, sizeof(recv_buf), mqtt_transport_read);
        if (ret == CONNACK) {
            unsigned char session_present, connack_rc;
            // “解包”CONNACK报文，检查返回码是否为0(成功)
            if (MQTTDeserialize_connack(&session_present, &connack_rc, recv_buf, ret) != 1 || connack_rc != 0) {
                printf_rtos("MQTT Connect failed, return code: %d\n", connack_rc);
                closesocket(g_mqtt_socket); g_mqtt_socket = -1;
                vTaskDelay(pdMS_TO_TICKS(MQTT_RECONNECT_DELAY_MS));
                continue;
            }
        } else {
            printf_rtos("Did not receive CONNACK.\n");
            closesocket(g_mqtt_socket); g_mqtt_socket = -1;
            vTaskDelay(pdMS_TO_TICKS(MQTT_RECONNECT_DELAY_MS));
            continue;
        }
        printf_rtos("MQTT Connected Successfully!\n");

        // ======================= 阶段 C: MQTT 订阅 =======================
        printf_rtos("Phase 3: MQTT Subscribing...\n");

        MQTTString topic_string = MQTTString_initializer;
        topic_string.cstring = (char*)MQTT_SUB_TOPIC;
        int sub_qos = 0; // 我们使用 QoS 0: 最多一次交付，最简单

        // “打包” SUBSCRIBE 报文并发送
        len = MQTTSerialize_subscribe(send_buf, sizeof(send_buf), 0, 1, 1, &topic_string, &sub_qos);
        mqtt_transport_write(send_buf, len);
        printf_rtos("Subscribed to topic: %s\n", MQTT_SUB_TOPIC);

        // ======================= 阶段 D: 数据收发与心跳维持 =======================
        printf_rtos("Phase 4: Entering main data loop...\n");
        // 使用独立的计时器


        // --- 检查并接收下行数据 ---
        // 为 recv 设置一个短暂的超时 (例如1秒)，这样循环就不会一直卡在 recv 上
        // 使得我们有机会执行下面的心跳和发布逻辑

        while(1) // 这个内部循环负责已连接状态下的所有工作
        {

            // 尝试读取一个完整的 MQTT 包
            ret = MQTTPacket_read(recv_buf, sizeof(recv_buf), mqtt_transport_read);
            if (ret > 0)
            {
                last_activity_tick=xTaskGetTickCount();
                // 判断收到的包类型
                if (ret == PUBLISH) // 如果是服务器转发来的 PUBLISH 消息
                {
                    unsigned char dup, retained;
                    int qos_rec;
                    unsigned short msgid;
                    MQTTString received_topic;
                    unsigned char* received_payload;
                    int payload_len;

                    // 1. 先调用辅助函数获取报文的准确总长度
                    int packet_len = get_mqtt_packet_length(recv_buf);

                    // 2. 将正确的长度传入反序列化函数
                    if (MQTTDeserialize_publish(&dup, &qos_rec, &retained, &msgid, &received_topic,
                                            &received_payload, &payload_len, recv_buf, packet_len) == 1)
                    {
                        // 打印收到的主题和消息内容
                        printf_rtos("<<< Recv PUBLISH on topic '%.*s': %.*s\n",
                                    received_topic.lenstring.len, received_topic.lenstring.data,
                                    payload_len, received_payload);
                    }
                }
                else if (ret== PINGRESP) // 如果是心跳回复
                {
                    printf_rtos("--- Ping Response received ---\n");
                }
                memset(recv_buf,'\0',sizeof(recv_buf));
            }
            // 如果读取出错，说明 TCP 连接已断开
            else if (ret < 0) {
                 printf_rtos("Socket read timeout\n");
            }

            // --- 检查是否需要发送心跳包 ---
            // 如果距离上次发送任何数据的时间超过了心跳间隔的一半，就发个心跳包
            if ((xTaskGetTickCount() - last_activity_tick) > pdMS_TO_TICKS(MQTT_KEEP_ALIVE_INTERVAL_S * 1000 / 2)) {
                // “打包” PINGREQ (心跳请求) 包并发送
                len = MQTTSerialize_pingreq(send_buf, sizeof(send_buf));
                ret = mqtt_transport_write(send_buf, len);
                if (ret < 0) {
                    printf_rtos("Failed to send PINGREQ, connection lost.\n");
                    break; // 发送失败，跳出循环去重连
                }
                printf_rtos("--- Ping Request sent ---\n");
                last_activity_tick = xTaskGetTickCount(); // 更新最后一次发送的时间
            }

            // --- 发布我们自己的遥测消息 ---
            char payload[64];
            if ((xTaskGetTickCount() - last_publish_tick) > pdMS_TO_TICKS(5000))
            {
                // ... 发布逻辑 ...
                // “打包” PUBLISH 报文
                snprintf(payload, sizeof(payload), "{\"count\": %lu}", count++);
                topic_string.cstring = (char*)MQTT_PUB_TOPIC;
                len = MQTTSerialize_publish(send_buf, sizeof(send_buf), 0, 0, 0, 0, topic_string, (unsigned char*)payload, strlen(payload));
                ret = mqtt_transport_write(send_buf, len);
                if (ret < 0) {
                    printf_rtos("Failed to publish, connection lost.\n");
                    break; // 发送失败，跳出循环去重连
                }
                printf_rtos(">>> Sent PUBLISH to %s: %s\n", MQTT_PUB_TOPIC, payload);
                last_publish_tick = xTaskGetTickCount();  // 发布也是一种网络活动
                last_activity_tick=last_publish_tick;
            }
        }

        // 如果代码能执行到这里，说明内部循环已跳出，代表连接已断开
        printf_rtos("Disconnecting and cleaning up...\n");
        closesocket(g_mqtt_socket);
        g_mqtt_socket = -1;
    }

}